// components/sticky/index.js
const { BaseBehavior, SelectQueryBehavior, COMPONENT_OPTIONS } = require('../../mixins/component');
const target = '.mini-sticky';
Component({
    behaviors: [BaseBehavior, SelectQueryBehavior],
    options: {
        ...COMPONENT_OPTIONS,
        // 指定所有 _ 开头的数据字段为纯数据字段
        pureDataPattern: /^_/,
    },
    properties: {
        // 距离顶部高度
        offsetTop: {
            type: Number,
            value: 0,
        },
        // 层级
        zIndex: {
            type: Number,
            value: 99,
        },
        // 是否禁用
        disabled: {
            type: Boolean,
            value: false,
        },
        // 容器
        container: {
            type: null,
        },
    },
    data: {
        fixed: false,
        height: 0,
        _attached: false,
        _containerHeight: 0,
    },
    observers: {
        disabled: function(newVal) {
            if (!this.data._attached) return;
            newVal ? this.disconnectObserver() : this.initObserver();
        },
    
        container: function(newVal) {
            if (typeof newVal !== 'function' || !this.data.height) return;
            this.observerContainer()
        },
    
        offsetTop: function(newVal){
            if(typeof newVal !== 'number' || !this.data._attached) return;
            this.initObserver();
        },
    },
    lifetimes: {
        attached() {
            this.data._attached = true;
            if (!this.data.disabled) this.initObserver();
        },
    
        detached() {
            this.data._attached = false;
            this.disconnectObserver();
        },
    },
    methods: {
        getContainerRect () {
            const nodesRef = this.data.container();
            return new Promise(resolve => nodesRef.boundingClientRect(resolve).exec());
        },
    
        initObserver() {
            this.disconnectObserver()
            this.getRect(target).then(rect => {
                this.setData({
                    height: rect.height,
                });
                this.observerContent();
                this.observerContainer();
            })
        },
        
        disconnectObserver(observerName) {
            if (observerName) {
                const observer = this[observerName];
                observer && observer.disconnect();
            } else {
                this.contentObserver && this.contentObserver.disconnect();
                this.containerObserver && this.containerObserver.disconnect();
            }
        },
    
        observerContent() {
            const {offsetTop} = this.data;
            this.disconnectObserver('contentObserver');
    
            const contentObserver = this.createIntersectionObserver({
                thresholds: [1],
                initialRatio: 1,
            });
            contentObserver.relativeToViewport({
                top: -offsetTop
            })
            contentObserver.observe(target, res => {
                if (this.data.disabled) return;
                this.setFixed(res.boundingClientRect.top);
            });
            this.contentObserver = contentObserver
        },
    
        observerContainer() {
            const {container, height, offsetTop} = this.data
            if (typeof container !== 'function') return;
    
            this.disconnectObserver('containerObserver');
            this.getContainerRect().then(rect => {
                this.getRect(target).then(contentRect => {
                    const _contentTop = contentRect.top;
                    const _containerTop = rect.top;
                    const _containerHeight = rect.height;
                    const _relativeTop = _contentTop - _containerTop;
                    const containerObserver = this.createIntersectionObserver({
                        thresholds: [1],
                        initialRatio: 1,
                    });
                    containerObserver.relativeToViewport({
                        top: _containerHeight - height - offsetTop - _relativeTop,
                    });
                    containerObserver.observe(target, (res) => {
                        if (this.data.disabled) return;
                        this.setFixed(res.boundingClientRect.top);
                    })
                    this.data._relativeTop = _relativeTop;
                    this.data._containerHeight = _containerHeight;
                    this.containerObserver = containerObserver;
                });
            });
        },
    
        setFixed(top) {
            const {height, _containerHeight, _relativeTop, offsetTop} = this.data;
            const fixed = _containerHeight && height
            ? (top >= height + offsetTop + _relativeTop - _containerHeight) && (top < offsetTop)
            : top < offsetTop;
            this.triggerEvent('scroll', {
                scrollTop: top,
                isFixed: fixed,
            });
    
            this.setData({fixed});
        }
    }
})
